package framework.ai;

/**
 * @author HB
 *
 */
public class TicTacToeAI extends AbstractAIModule {
	
	private final static int BOARD_WIDTH = 3;
	private final static int BOARD_HEIGHT = 3;
	private final static String GAME_TYPE = "Tic Tac Toe";
	
	public TicTacToeAI() {
		super(BOARD_WIDTH, BOARD_HEIGHT, GAME_TYPE);
	}
	
	/**
	 * get best move by using miniMax method
	 * and parse best row and column to one integer
	 * @return Integer move
	 */
	@Override
	public Integer getBestMove(int player) {
		BestMove best = chooseMove(player);
		int move = 0;
		
		if (best != null){
			move = (best.getRow() * board.length) + best.getCol();
		}
		
		return move;
	}
	
	/**
	 * calculate bestmove using minimax 
	 * @param player
	 * @return BestMove
	 */
	private BestMove chooseMove(int player){
		int opp;
		BestMove reply;
		int simpleEval;
		int bestRow = 0;
		int bestCol = 0;
		int value;
		
		if((simpleEval = getState(player)) != UNKNOWN){
			return new BestMove(simpleEval);
		}
		
		if (player == P1){
			opp = P2;
			value = LOSE;
		}else{
			opp = P1;
			value = LOSE;
		}
		
		for (int row = 0; row < board.length; row++){
			for (int col = 0; col < board.length; col++){
				if(board[row][col] == EMPTY){
					place(row, col, player);		//perform move
					reply = chooseMove(opp); // opp move
					place(row, col, EMPTY); // undo move
					if(((player == P1) && (reply.val > value)) || ((player == P2) && (reply.val > value))){ 
						// beurt pc als value groter is dan opp value geef beste zet
						//beurt mens opp valeu kleiner dan pc value geef beste zet
						bestRow = row;
						bestCol = col;
						value = reply.val;
					}
				}
			}
		}
		return new BestMove(value, bestRow, bestCol);
	}

	
	/**
	 * perform move
	 */
	@Override
	public void doMove(int player, int move) {
		
		int row = move / board.length;
		int col = move % board.length;
		
		if(isValidMove(player, move)){
			board[row][col] = player;
		}
	}
	
	/**
	 * 
	 * @param row
	 * @param col
	 * @param player
	 */
	private void place(int row, int col, int player){
		board[row][col] = player;
	}

	/**
	 * check if selected move is valid
	 * @return true when valid
	 */
	@Override
	public boolean isValidMove(int player, int move) {
		if(player == super.getCurrentPlayer()) {
			if(move >= 0 && move <= 8 && board[move / board.length][move % board.length] == EMPTY){
				return true;
			}
		}
		return false;
	}

	/**
	 * get the state of the current match
	 * @return int with state of current player
	 */
	@Override
	protected int getState(int player) {
		if(boardIsFull()){
			return DRAW;
		}else if(isAWin(player)){
			return WIN;
		}else if(isAWin(super.getOpponent())){
			return LOSE;
		}else{
			return UNKNOWN;
		}
	}
	
	/**
	 * check if board is full 
	 * @return true when full
	 */
	private boolean boardIsFull(){
		for (int row = 0; row < board.length; row++){
			for(int col = 0; col < board.length; col++){
				if (board[row][col] == EMPTY){
					return false;
				}
			}
		}
		return true;
	}
	
	/**
	 * check if player has won.
	 * @param player
	 * @return true when player has won
	 */
	private boolean isAWin(int player){
		if(checkHorizontal(player) || checkVertical(player)){
			return true;
		}else if(checkDiagonal(player, 0, 0, 1, 1)){
			return true;			
		}else if (checkDiagonal(player, 0, board.length - 1, 1, -1)){
			return true;
		}
		return false;
	}
	
	/*
	 **********************************************************
	 ***	check if win for all different directions		***					
	 **********************************************************
	 */
	/**
	 * horizontal win
	 * @param player
	 * @return true when win
	 */
	private boolean checkHorizontal(int player){
		for(int row = 0; row<board.length; row++){
			for(int col = 0; col<board.length; col++){
				if(board[row][col] == EMPTY ||board[row][col] != player){
					return false;
				}
			}
		}
		return true;
	}
	
	/**
	 * vertical win
	 * @param player
	 * @return true when win 
	 */
	private boolean checkVertical(int player){
		for(int col = 0; col<board.length; col++){
			for(int row = 0; row<board.length; row++){
				if(board[row][col] == EMPTY ||board[row][col] != player){
					return false;
				}
			}
		}
		return true;
	}
	
	/**
	 * check diagonal win
	 * @param player
	 * @param startRow
	 * @param startCol
	 * @param offsetRow
	 * @param offsetCol
	 * @return true when win
	 */
	private boolean checkDiagonal(int player, int startRow, int startCol, int offsetRow, int offsetCol){
		int row = startRow;	
		int col = startCol;
		for(int i = 0; i < board.length;i++){
			if(board[row][col] != player){
				return false;
			}
			row+=offsetRow;
			col+=offsetCol;
		}
		return true;
	}
}
